require "lib.base"
local template = {}

--compile template
function template:compile(file_name, cache)
    cache = cache or false
    if not cache or not self[file_name.."compiled"] then
        local insert_code = self:include(file_name, cache)
        table.insert(insert_code, 1, string.format("local compiled_func = function(data)  local ___generated_code = {}", string.split(file_name, "%.")[1]))
        table.insert(insert_code, "return table.concat(___generated_code) end return compiled_func")
        local code = table.concat(insert_code, " ")
        local compiled_func = assert(load(code))
        self[file_name.."compiled"] = compiled_func()
    else
        print("hit template cache.")
    end
end

function template:include(file_name, cache)
    cache = cache or false
    if not cache or not self[file_name] then
        local generated_code = {}
        local fp = assert(io.open(file_name, "rb"))
        local content = fp:read('*a')
        fp:close()
        local end_pos = 0
        for a, b, c, d, e in string.gmatch(content, "(.-)({{)(.-)(}})()") do
            end_pos = e
            table.insert(generated_code, string.format("table.insert(___generated_code, [[%s]])", a))
            if string.byte(c) == string.byte('=') then --variable value
                local var = string.sub(c, 2)
                table.insert(generated_code, string.format("table.insert(___generated_code, %s)", var))
            elseif string.byte(c) == string.byte('@') then --include push pop
                local method = string.sub(c, 2, string.find(c, "%s") - 1)
                local param = string.trim(string.sub(c, string.find(c, "%s") + 1))
                if method == "include" then
                    local insert_table = self:include(param, cache)
                    for _, line in ipairs(insert_table) do
                        table.insert(generated_code, line)
                    end
                elseif method == "push" then
                    table.insert(generated_code, string.format("<!--push-template:%s-->", param))
                    --table.insert(generated_code, string.format("___stack_map[\"%s\"] = #___generated_code", param)
                elseif method == "pop" then
                    table.insert(generated_code, string.format("local ___current_template = %s  local ___backup_generated_code = ___generated_code "))
                end
            else
                table.insert(generated_code, c)
            end
        end
        if end_pos < string.len(content) then
            table.insert(generated_code, string.format("table.insert(___generated_code, [[%s]])", string.sub(content, end_pos)))
        end
        self[file_name] = generated_code
    end
    return self[file_name]
end

function template:handle_template(file_name, context)
    self:compile(file_name, true)
    return self[file_name.."compiled"](context)
end

return template
